using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using PulseLibrary;
using PulsePrototype.PulseManagers;
using PulsePrototype.PulseManagers.Components;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics;
using PulsePrototype.Messages;
using PulsePrototype.Components;


namespace PulsePrototype
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class SceneManager : BaseManager
    {
        private Dictionary<String, BaseEntity> entities;
        private static int uniqueEntityIDs = 0;
        private BaseEntity targetEntity;

        public Dictionary<String, BaseEntity> Entities
        {
            get { return entities; }
        }

        public ContentManager Content
        {
            get { return Game.Content; }
        }

        protected override string GetName()
        {
            return "Scene";
        }

        public SceneManager(Game1 game)
            : base(game)
        {
            // TODO: Construct any child components here
            this.entities = new Dictionary<string, BaseEntity>();
            Initialize();
        }

        public override void LoadContent()
        {
            // We always have at least one entity in the world. We'll create that now and then
            // make it our main camera for the game.
            BaseEntity camEntity = new BaseEntity(this, Common.RootEntityName, uniqueEntityIDs++);

            // The component will automatically attach to the entity that we pass into it
            CameraComponent camComp = new CameraComponent(camEntity);

            //Now we add the entity to the scene
            AddEntityToScene(camEntity);

            // ===================================================================================
            // TEMPORARY!!!!!!!!!!!!!!!!!!!!!!!!!!
            BaseEntity backgroundEntity = new BaseEntity(this, "Background", uniqueEntityIDs++);
            backgroundEntity.position = Vector3.Zero;
            RenderComponent backgroundRendComp = new RenderComponent(backgroundEntity, "Background/board");
            backgroundRendComp.Description.scale = new Vector2(0.8f, 0.8f);
            backgroundRendComp.Description.transparency = 0.2f;

            AddEntityToScene(backgroundEntity);

            PhysicsManager physicsMan = (PhysicsManager)Game.GamePlayScreen1.GetManager("Physics");
            // Interesting tidbit about PhysicsComponent: as soon as you instantiate one, 
            // it creates a Body variable without needing an entity to be added to the scene.

            BaseEntity testArrow = new BaseEntity(this, "ArrowHead", uniqueEntityIDs++);
            testArrow.position = new Vector3(0, -200, 0);
            RenderComponent rendComp = new RenderComponent(testArrow, "pixelbox");
            rendComp.Description.Destination = new Rectangle(0, -200, 70, 70);
            
            //PhysicsComponent physComp = new PhysicsComponent(testArrow, physicsMan.World,
            //    new Vector2(64, 128), ConvertUnits.ToSimUnits(testArrow.position.ToVector2()), 3.0f);
            PhysicsComponent physComp = new PhysicsComponent(testArrow, physicsMan.World, new Vector2(70, 70), 
                ConvertUnits.ToSimUnits(testArrow.position.ToVector2()));
            physComp.MainBody.FixedRotation = true;

            ControlComponent contComp = new ControlComponent(testArrow);

            AddEntityToScene(testArrow);

            BaseEntity testArrow1 = new BaseEntity(this, "ArrowHead1", uniqueEntityIDs++);
            testArrow1.position = new Vector3(0, 18, 0);
            RenderComponent rendComp1 = new RenderComponent(testArrow1, "arrow");
            rendComp1.Description.scale = new Vector2(2, 2);

            AddEntityToScene(testArrow1);

            // Creating a rotating arrow platform

            BaseEntity testArrow2 = new BaseEntity(this, "ArrowHead2", uniqueEntityIDs++);
            testArrow2.position = new Vector3(20, 80, 0);
            RenderComponent rendComp2 = new RenderComponent(testArrow2, "arrow");
            rendComp2.Description.scale = new Vector2(1.5f, 1f);

            PhysicsComponent physComp1 = new PhysicsComponent(testArrow2, physicsMan.World,
                new Vector2(rendComp2.Description.Destination.Width, rendComp2.Description.Destination.Height),
                Vector2.Zero); // Where the body is doesn't matter, making its axle overrides it.

            Body axle = BodyFactory.CreateCircle(physicsMan.World, 0.1f, 1f, 
                new Vector2(0f, ConvertUnits.ToSimUnits(80))); // position of joint
            
            // to prevent any other bodies from colliding with the axle itself
            axle.IsSensor = true;
  
            var j = JointFactory.CreateRevoluteJoint(
                physicsMan.World,
                physComp1.MainBody,
                axle,
                new Vector2(0f, 0f),
                new Vector2(0f, 0f)); // position of where the body connects to the axle

            //j.MotorSpeed = MathHelper.Pi;
            //j.MotorImpulse = 100;
            //j.MotorEnabled = true;
            //j.MaxMotorTorque = 100;  

            AddEntityToScene(testArrow2);

            BaseEntity testGem = new BaseEntity(this, "Gem", uniqueEntityIDs++);
            testGem.position = new Vector3(0, 0, 0);
            RenderComponent rendComp3 = new RenderComponent(testGem, "GUI/gem");

            AddEntityToScene(testGem);

            // Creating a floor for objects to land on.

            BaseEntity floor = new BaseEntity(this, "Floor", uniqueEntityIDs++);
            floor.position = new Vector3(0, Game.GraphicsDevice.Viewport.Height/2, 0);

            PhysicsComponent physComp2 = new PhysicsComponent(floor, physicsMan.World,
                new Vector2(Game.GraphicsDevice.Viewport.Width, 1),
                ConvertUnits.ToSimUnits(floor.position.ToVector2()));

            physComp2.MainBody.BodyType = BodyType.Static;

            AddEntityToScene(floor);

            entities.TryGetValue("ArrowHead", out targetEntity);
            // ===================================================================================
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        public void AddEntityToScene(BaseEntity entity)
        {
            BaseEntity checkEntity = null;
            if (entities.TryGetValue(entity.Name, out checkEntity))
            {
                throw new Exception("An entity named " + entity.Name + " already exists.");
            }

            entities.Add(entity.Name, entity);
        }

        public void RemoveEntityFromScene(BaseEntity entity)
        {
            BaseEntity checkEntity = null;
            if (!entities.TryGetValue(entity.Name, out checkEntity))
            {
                throw new Exception("No entity named " + entity.Name + " exist in the scene to be removed.");
            }

            entities.Remove(entity.Name);
        }

        public BaseEntity GetEntity(String entityName)
        {
            BaseEntity entity = null;
            if (!entities.TryGetValue(entityName, out entity))
            {
                // Entity wasn't found
            }

            return entity;
        }

        /// <summary>
        /// Gets a list of entities based on name. 
        /// Returns an empty list if no entity is found. 
        /// Set 'contains' if you want to find all entities that contain the 'name'.
        /// </summary>
        public IEnumerable<KeyValuePair<String, BaseEntity>> GetEntity(string name, bool contains)
        {
            return this.entities.Where(e => contains ? e.Value.Name.Contains(name) : e.Value.Name.Equals(name));
        }

        // Returns true if the entity or any components handled the message
        public bool SendMessage(BaseMessage msg)
        {
            // We look for the entity in the scene by its ID
            BaseEntity entity;
            foreach (KeyValuePair<String, BaseEntity> ent in Entities)
            {
                entity = ent.Value;
                if (entity.UniqueID == msg.destEntityID)
                {
                    return entity.SendMessage(msg);
                }
            }

            // Entity with the specified ID wasn't found
            return false;
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            foreach (KeyValuePair<String, BaseEntity> pair in entities) {
                (pair.Value).Update(gameTime); }

            // Follow the targetEntity. 
            // The following code is just being used to test the camera.
            if (targetEntity != null)
            {
                BaseEntity camEntity = GetEntity(Common.RootEntityName);
                CameraComponent cam = camEntity.GetComponent<CameraComponent>();

                cam.camPosition = targetEntity.position;
            }

            base.Update(gameTime);
        }
    }
}
